跳到主要内容

FakeLocation 逆向分析

· 阅读需 6 分钟
声明

本文版权归原作者所有,转载请注明出处。

前言

看到一篇文章:https://www.52pojie.cn/thread-1955462-1-1.html,是有关 FakeLocation 的逆向分析,以前大学的时候用这个软件还帮别人代跑呢,那时候一直用的别人的破解版,现在跟着 52 的师傅(以下称 mengxinb 师傅)来复现研究一下, 分享并记录以便提高动手能力。

脱壳

一直以来就采用的 360 加固,正常情况 360 加固在模拟器中会闪退,但因为很多人都在模拟器上跑步,因此作者可能加固的时候没开这个选项,所以也可以在模拟器中复现,但是我手机有 root 就直接开干吧。

|300

这里我采用软件君子(B 站主页:https://space.bilibili.com/638226254)视频中分析的 frida 脱壳脚本脱壳(不知道还能活多久),这种方式拖的比较全,前提是没有检测 frida 或者你能绕过,脚本如下:

eval(function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1;};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p;}('7 1g(){2 1f=a.g(\'h.8\',\'M\');2 M=c i(1f,\'6\',[\'e\',\'6\']);2 15=a.g("h.8","11");2 11=c i(15,"6",["6","e","6"]);2 19=a.g(\'h.8\',\'w\');2 w=c i(19,\'6\',[\'6\']);2 4=y.K("/1u/1r/1s");2 9=M(4,0);3(9!=-1){2 U=y.1x(1b);2 W=11(9,U,1b);w(9);W=f(U).E();Z W}Z"-1"}7 u(4){2 16=a.g(\'h.8\',\'u\');2 u=c i(16,\'6\',[\'e\',\'6\']);2 14=a.g(\'h.8\',\'X\');2 X=c i(14,\'e\',[\'e\']);2 17=a.g(\'h.8\',\'T\');2 T=c i(17,\'6\',[\'e\']);2 q=y.K(4);2 V=X(q);3(V!=0){T(V);Z 0}u(q,1a);o(4)}7 o(4){2 18=a.g(\'h.8\',\'o\');2 o=c i(18,\'6\',[\'e\',\'6\']);2 q=y.K(4);o(q,1a)}7 G(){2 s=B.1G("s.8");2 p=k;2 O=s.1B();1A(2 x=0;x<O.1z;x++){2 v=O[x];2 m=v.1C;3(m.d("1F")>=0&&m.d("1m")>=0&&m.d("1E")>=0&&m.d("1D")>=0){n.l(m,v.1k);p=v.1k}}2 Q={};2 A=1;n.l("[1m:]",p);3(p){13.10(p,{12:7(j){2 N=j[5];2 t=f(N).1h(B.P).1L();2 Y=f(N).1h(B.P+B.P).1M();3(Q[t]==L){Q[t]=Y;2 1e=f(t).E();3(1e.d("H")==0){2 z=1g();3(z!="-1"){2 S="/1i/1i/"+z+"/1J/1N"+z;u(S);2 I=S+"/1I"+(A==1?"":A)+".H";n.l("[1K H]:",I);2 9=c 1H(I,"1o");3(9&&9!=k){A++;2 1l=f(t).1q(Y);9.1p(1l);9.1n();9.w();n.l("[1w H]:",I)}}}}},J:7(R){}})}}2 r=1v;7 1y(){13.10(a.1d(k,"1c"),{12:7(j){2 b=j[0];3(b!==L&&b!=k){2 4=f(b).E();3(4.d("s.8")>=0){D.C=F;n.l("[1c:]",4)}}},J:7(R){3(D.C&&!r){G();r=F}}});13.10(a.1d(k,"1j"),{12:7(j){2 b=j[0];3(b!==L&&b!=k){2 4=f(b).E();3(4.d("s.8")>=0){D.C=F;n.l("[1j:]",4)}}},J:7(R){3(D.C&&!r){G();r=F}}})}1t(G);',62,112,'||var|if|path||int|function|so|fd|Module|pathptr|new|indexOf|pointer|ptr|getExportByName|libc|NativeFunction|args|null|log|symbol_name|console|chmod|addr_DefineClass|cPath|is_hook_libart|libart|base|mkdir|symbol|close|index|Memory|process_name|dex_count|Process|can_hook_libart|this|readCString|true|dump_dex|dex|dex_path|onLeave|allocUtf8String|undefined|open|dex_file|symbols|pointerSize|dex_maps|retval|dex_dir_path|closedir|buffer|dir|result|opendir|size|return|attach|read|onEnter|Interceptor|opendirPtr|readPtr|mkdirPtr|closedirPtr|chmodPtr|closePtr|755|0x1000|dlopen|findExportByName|magic|openPtr|get_self_process_name|add|data|android_dlopen_ext|address|dex_buffer|DefineClass|flush|wb|write|readByteArray|self|cmdline|setImmediate|proc|false|dump|alloc|hook_dlopen|length|for|enumerateSymbols|name|DexFile|Thread|ClassLinker|findModuleByName|File|class|files|find|readPointer|readUInt|dump_dex_'.split('|'),0,{}))

具体原理我也没研究,先用着,使用算法助手进行导入脚本进行脱壳:

|400

启用并打开应用,显示注入成功,这个时候就已经脱完了:

|225

脱壳的 dex 在 /data/data/<包名>/files/dump_dex_<包名> 下,也就是 /data/data/com.lerist.fakelocation/files/dump_dex_com.lerist.fakelocation

|300

打包成 zip 文件,用 jadx 打开,为后续逆向分析做准备:

|350

控件跟踪

使用专业版功能时(如路线模拟),会出现需要专业版的提示。

|300

根据 mengxinb 师傅的思路,使用算法助手去尝试打印控件的堆栈信息,去追踪判断专业版的逻辑。

|300

启动软件,首先点击路线模拟,然后点击启动模拟,会出现专业版弹窗提示:

|175

查看算法助手日志,搜索关键词专业版,随便点击一个查看详情:

|275

可以看到有一个 onClick 方法,也就是点击启动模拟的时候回调,软件肯定需要判断你是不是专业版,按照这个逻辑,我们先追踪一下它的后面一个方法,也就是图中的方法,长按复制整行:

|325

jadx 中搜索方法名:

|350

可以看到方法名为 m9662,因为软件带了混淆,jadx 默认就开启了反混淆将一些难以阅读的变量和方法名重命名了,看起来没那么难读,这些并不是真实在软件中的方法名。

这个时候用同样的方式搜索 onClick 方法并点击进入,可以看到红框部分的判断:

|375

代码如下:

if (!C1834.m9387()) {
C2263.m9662(c24082.getView(), -2);
return;
}

Frida Hook

其中 m9662 方法是未开通专业版时才调用,因此反过来开通了专业版则不会调用该方法,将 C1834.m9387() 的返回值改为 True,使用 frida 进行 Hook:

function hook() {
Java.perform(()=>{
let C1834 = Java.use("\u0781.\u0783.\u0620.\u0620.\u058F");
C1834["\u0785"].implementation = function () {
console.log(`C1834.m9387 is called`);
let result = this["\u0785"]();
console.log(`C1834.m9387 result=${result}`);
result = true
return result;
};
})
}

setTimeout(hook,1000);

成功绕过专业版功能限制:

|300

双向证书验证

Mengxinb 师傅还分析了如何通过分析网络请求来绕过专业版功能限制,但Fakelocation 使用了双向证书来校验,mengxinb 师傅使用的是手动分析,而我一般首先习惯使用大佬们的脚本。

使用 r0capture 脚本 dump 证书

一把梭:

python r0capture.py -H 127.0.0.1:28042 -f com.lerist.fakelocation

|500

脚本默认开启 dump 证书:

打开并使用 XCA 导入搞定:

致谢

感谢大佬们开发的工具及脚本:

https://mp.weixin.qq.com/s/JPzjpQ3ZoY7cd5K1ZywTZA

https://github.com/r0ysue/r0capture

mengxinb 师傅的文章:

https://www.52pojie.cn/thread-1955462-1-1.html

软件君子主页:

https://space.bilibili.com/638226254